package com.gframework.sqlparam;

import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
import java.util.Map;

import javax.persistence.Column;

import org.apache.commons.lang3.ClassUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.StringUtils;

import com.gframework.util.GStringUtils;
import com.gframework.util.LambdaUtils;

/**
 * lambda表达式获取列名称工具类.
 * <p>本类会缓存名称
 * @since 2.0.0
 * @author Ghwolf
 *
 */
class LambdaColumnNameUtil {
	
	private LambdaColumnNameUtil(){}
	
	private static final Map<String,String> nameCache = new ConcurrentReferenceHashMap<>();
	
	/**
	 * 获取一个getter方法的映射数据库列名称.
	 * <p>会自动去掉开头的is和get，并且剩下的部分会按照驼峰转下换线命名方式进行转换。
	 * <p>但是如果有{@link javax.persistence.Column}注解，则会读取此注解的name
	 */
	static String getColumnName(Serializable supplier){
		return nameCache.computeIfAbsent(supplier.getClass().getName(), k -> init(supplier));
	}	
	
	private static String init(Serializable supplier){
		SerializedLambda ser = LambdaUtils.getSerializedLambda(supplier);
		String className = ser.getImplClass().replace('/', '.');
		String methodName = ser.getImplMethodName();
		Class<?> cls;
		try {
			cls = ClassUtils.getClass(className);
		} catch (ClassNotFoundException e) {
			throw new UnsupportedOperationException(e);
		}
		
		Method method = BeanUtils.findMethod(cls, methodName);
		Column col = AnnotationUtils.findAnnotation(method, Column.class);
		if (col != null && !StringUtils.isEmpty(col.name())) {
			return col.name();
		}
		
		if (methodName.startsWith("is")) {
			if (methodName.length() > 2 && Character.isUpperCase(methodName.charAt(2))) {
				methodName = methodName.substring(2);
			}
		} else if (methodName.startsWith("get")) {
			if (methodName.length() > 3 && Character.isUpperCase(methodName.charAt(3))) {
				methodName = methodName.substring(3);
			}
		}
		return GStringUtils.toMapUnderscore(methodName);
	}
	
}
